如何使用OIDC进行角色SSO

您所在的位置:网站首页 阿里云sso ak暴露 导致 如何使用OIDC进行角色SSO

如何使用OIDC进行角色SSO

2024-05-31 21:06| 来源: 网络整理| 查看: 265

本文提供一个Okta与阿里云进行OIDC角色SSO的示例,使Okta中的应用通过临时身份凭证(STS Token)安全访问阿里云资源。

前提条件请提前在Okta中注册一个OIDC应用,并获取应用的颁发者URL和客户端ID(Client ID)。本示例中使用的数据如下:颁发者URL:https://dev-xxxxxx.okta.com客户端ID:0oa294vi1vJoClev****步骤一:在阿里云创建OIDC身份提供商

本步骤中将创建一个名为TestOidcProvider的OIDC身份提供商。颁发者URL为https://dev-xxxxxx.okta.com,客户端ID为0oa294vi1vJoClev****。

使用阿里云账号登录RAM控制台。在左侧导航栏,选择集成管理 > SSO管理。在角色SSO页签,先单击OIDC页签,然后单击创建身份提供商。在创建身份提供商页面,设置身份提供商信息。参数说明身份提供商名称同一个阿里云账号下必须唯一。颁发者URL颁发者URL由外部IdP提供。颁发者URL必须以https开头,符合标准URL格式,但不允许带有query参数(以?标识)、fragment片段(以#标识)和登录信息(以@标识)。验证指纹为了防止颁发者URL被恶意劫持或篡改,您需要配置外部IdP的HTTPS CA证书生成的验证指纹。阿里云会辅助您自动计算该验证指纹,但是建议您在本地自己计算一次(例如:使用OpenSSL计算指纹),与阿里云计算的指纹进行对比。如果对比发现不同,则说明该颁发者URL可能已经受到攻击,请您务必再次确认,并填写正确的指纹。客户端ID您的应用在外部IdP注册的时候,会生成一个客户端ID(Client ID)。当您从外部IdP申请签发OIDC令牌时必须使用该客户端ID,签发出来的OIDC令牌也会通过aud字段携带该客户端ID。在创建OIDC身份提供商时配置该客户端ID,然后在使用OIDC令牌换取STS Token时,阿里云会校验OIDC令牌中aud字段所携带的客户端ID与OIDC身份提供商中配置的客户端ID是否一致。只有一致时,才允许扮演角色。

如果您有多个应用需要访问阿里云,您可以配置多个客户端ID,但最多不能超过20个。

备注身份提供商的描述信息。单击确定。步骤二:在阿里云创建可信实体为OIDC身份提供商的RAM角色

本步骤中将创建一个名为testoidc的RAM角色,身份提供商选择步骤一创建的TestOidcProvider。

使用阿里云账号登录RAM控制台。在左侧导航栏,选择身份管理 > 角色。在角色页面,单击创建角色。在创建角色面板,选择可信实体类型为身份提供商,然后单击下一步。输入角色名称和备注。选择身份提供商类型为OIDC。选择身份提供商并设置限制条件,然后单击完成。 支持的限制条件如下表所示:限制条件关键字说明是否必选示例oidc:issOIDC颁发者(Issuer)。用来扮演角色的OIDC令牌中的iss字段值必须满足该限制条件要求,角色才允许被扮演。

该限定条件必须使用StringEquals作为条件操作类型,条件值只能是您在OIDC身份提供商中填写的颁发者URL。该限制条件用于确保只有受信颁发者颁发的OIDC令牌才能扮演角色。

是https://dev-xxxxxx.okta.comoidc:audOIDC受众(Audience)。用来扮演角色的OIDC令牌中的aud字段值必须满足该限制条件要求,角色才允许被扮演。

该限定条件必须使用StringEquals作为条件操作类型,您可选择在OIDC身份提供商中配置的一个或多个客户端ID(Client ID)作为条件值。该限制条件用于确保只有您设置的Client ID生成的OIDC令牌才能扮演角色。

是0oa294vi1vJoClev****oidc:subOIDC主体(Subject)。用来扮演角色的OIDC令牌中的sub字段值必须满足该限制条件要求时,角色才允许被扮演。

该限定条件可以使用任何String类的条件操作类型,且您可以最多设置10个OIDC主体作为条件值。该限制条件用于进一步限制允许扮演角色的身份主体,您也可以不指定该限制条件。

否00u294e3mzNXt4Hi****单击关闭。步骤三:为RAM角色授权

您可以根据实际需要,为步骤二创建的RAM角色testoidc授予访问阿里云资源的权限。

使用阿里云账号登录RAM控制台。在左侧导航栏,选择身份管理 > 角色。在角色页面,单击目标RAM角色操作列的添加权限。在添加权限面板,为RAM角色添加权限。选择授权范围。整个云账号:权限在当前阿里云账号内生效。指定资源组:权限在指定的资源组内生效。说明 指定资源组授权生效的前提是该云服务已支持资源组。更多信息,请参见支持资源组的云服务。输入授权主体。授权主体即需要授权的RAM角色,系统会自动填入当前的RAM角色,您也可以添加其他RAM角色。选择权限策略。说明 每次最多绑定5条策略,如需绑定更多策略,请分次操作。单击确定。单击完成。步骤四:在Okta签发OIDC令牌(OIDC Token)

阿里云不支持使用OIDC登录控制台,所以您需要使用程序访问的方式完成OIDC SSO流程。由于生成OIDC Token本质上是个OAuth流程,所以您需要通过标准的OAuth 2.0流程从OIDC IdP(例如:Okta)获取OIDC Token。OAuth支持多种流程,例如:比较常见的Authorization Code Flow。但由于该流程较为复杂,为演示方便,如下将以比较简单的Implicit Flow为例,为您介绍获取OIDC Token并最终完成SSO的流程,其中简化了标准协议要求的部分步骤。

搭建一个客户端Web应用,用于接收Okta颁发的OIDC Token。本示例中,将提供一个使用Java Spring Boot和Thymeleaf搭建的极简客户端Web应用。在本机8080端口部署Web应用,绑定的localhost指向127.0.0.1,因此在本机通过浏览器访问localhost:8080就可以访问到该Web应用。相关的示例代码如下:静态页面示例代码

按照OAuth 2.0协议要求Okta回调给客户端Web应用的信息是通过锚点(fragment)来传递的,您可以通过一个Web页面,直接提取出锚点参数来获取回调的OIDC Token。假设您制作了如下这个简单的静态页面,直接进行参数透传。该页面的完整地址为http://localhost:8080/accessTokenCallback,也就是Okta应用配置的回调地址redirect_uri。

window.onload = function () { let fragment = window.location.hash.substring(1); window.location.href = "/receiveAccessToken?" + fragment; }; 类示例代码

创建一个类,作为上述静态页面的控制器。

package com.aliyun.oauthtest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class CallbackController { @RequestMapping("accessTokenCallback") public String callback() { return "accessTokenCallback"; } }登录Okta,向Okta申请签发OIDC Token。您需要先登录Okta,然后基于步骤1搭建的客户端Web应用,直接构造并访问URL:https://dev-xxxxxx.okta.com/oauth2/v1/authorize?client_id=0oa294vi1vJoClev****&scope=openid&response_type=token%20id_token&state=testState&nonce=a_unique_nonce_1&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2FaccessTokenCallback

参数含义如下:

client_id:Okta中注册的OIDC应用的客户端ID。scope:取值为openid。response_type:Implicit Flow流程中取值为token id_token。state:表示客户端的当前状态,可以指定任意值。nonce:防止重放攻击,可以指定任意值。redirect_uri:接收access_token或id_token的回调地址,即步骤1中的客户端Web应用的地址。

本示例中已经预先登录了Okta,所以系统会根据用户设置的redirect_uri重定向到回调地址。如下地址中的id_token就是OIDC Token。

HTTP/1.1 302 Found Location: http://localhost:8080/accessTokenCallback#id_token=eyJraWQiOiJ6OUV0e****&access_token=eyJraWQiOiJseEQ3R****&token_type=Bearer&expires_in=3600&scope=openid&state=testState解析OIDC Token。

您可以对步骤2获取的结果进行简单地解析,将header和payload展开。

请求示例:

package com.aliyun.oauthtest; import java.util.Base64; import java.util.Base64.Decoder; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ClientAppController { @RequestMapping(value = "/receiveAccessToken", method = {RequestMethod.POST, RequestMethod.GET}, produces = "application/json") public Map receiveAccessToken(@RequestParam("access_token") String accessToken, @RequestParam("id_token") String idToken, @RequestParam("token_type") String tokenType, @RequestParam("expires_in") Long expireTime, @RequestParam("scope") String scope, @RequestParam("state") String state) { Map result = new TreeMap(); result.put("access_token", accessToken); result.put("id_token", idToken); result.put("token_type", tokenType); result.put("expires_in", "" + expireTime); result.put("scope", scope); result.put("state", state); String[] jwt = idToken.split("\\."); Decoder decoder = Base64.getDecoder(); result.put(" id token jwt header", JSON.parse(new String(decoder.decode(jwt[0])))); result.put(" id token jwt payload", JSON.parse(new String(decoder.decode(jwt[1])))); result.put(" id token jwt signature", jwt[2]); return result; } }

返回示例:

{ " id token jwt header": { "kid": "z9EtyT345d-JLIJo2-5ySDO27LG4FPeOotbwJPT****", "alg": "RS256" }, " id token jwt payload": { "at_hash": "KKsdN3prZWTvBEMn-g****", "sub": "00u294e3mzNXt4Hi****", "aud": "0oa294vi1vJoClev****", "ver": 1, "idp": "0oa294iehxjUCZIO****", "amr": [ "pwd" ], "auth_time": 1636373097, "iss": "https://dev-xxxxxx.okta.com", "exp": 1636377759, "iat": 1636374159, "nonce": "a_unique_nonce_1", "jti": "ID.lmSU5AD2iKLCVu6_KLMIr52dpCprncxW38v-NCA****" }, "id token jwt signature": "ZEJEGIv4Zoau63****", "access_token": "eyJraWQiOiJseEQ3R****", "expires_in": "3600", "id_token": "eyJraWQiOiJ6OUV0e****", "scope": "openid", "state": "testState", "token_type": "Bearer" }步骤五:使用OIDC Token换取STS Token

您可以直接调用AssumeRoleWithOIDC API,使用从步骤四获取的未解析的OIDC Token换取STS Token。

请求示例:

public static void main(String[] args) { IAcsClient client = initialization(); String jwtToken = "eyJraWQiOiJ6OUV0e****"; //从Okta获取的未解析的id_token。 AssumeRoleWithOIDCRequest request = new AssumeRoleWithOIDCRequest(); request.setDurationSeconds(3600L); request.setOIDCProviderArn("acs:ram::113511544585****:oidc-provider/TestOidcProvider"); request.setOIDCToken(jwtToken); request.setRoleArn("acs:ram::113511544585****:role/testoidc"); request.setRoleSessionName("TestOidcAssumedRoleSession"); try { AssumeRoleWithOIDCResponse resp = client.getAcsResponse(request); System.out.println("success requestId: " + resp.getRequestId()); System.out.println("success assume role arn: " + resp.getAssumedRoleUser().getArn()); System.out.println("success sts credential accessKey id: " + resp.getCredentials().getAccessKeyId()); System.out.println("success sts credential accessKey secret: " + resp.getCredentials().getAccessKeySecret()); System.out.println("success resp: " + JSON.toJSONString(resp)); } catch(ClientException | SystemException e) { e.printStackTrace(); } }

返回示例:

success requestId: 3D57EAD2-8723-1F26-B69C-F8707D8B565D success assume role arn: acs:ram::113511544585****:role/testoidc/TestOidcAssumedRoleSession success sts credential accessKey id: STS.NUgYrLnoC37mZZCNnAbez**** success sts credential accessKey secret: CVwjCkNzTMupZ8NbTCxCBRq3K16jtcWFTJAyBEv2**** success resp: { "AssumedRoleUser": { "Arn": "acs:ram::113511544585****:role/testoidc/TestOidcAssumedRoleSession", "AssumedRoleId": "33157794895460****:TestOidcAssumedRoleSession" }, "Credentials": { "AccessKeyId": "STS.NUgYrLnoC37mZZCNnAbez****", "AccessKeySecret": "CVwjCkNzTMupZ8NbTCxCBRq3K16jtcWFTJAyBEv2****", "Expiration": "2021-10-20T04:27:09Z", "SecurityToken": "CAIShwJ1q6Ft5B2yfSjIr****" }, "OIDCTokenInfo": { "ClientIds": "0oa294vi1vJoClev****", "Issuer": "https://dev-xxxxxx.okta.com", "Subject": "00u294e3mzNXt4Hi****" }, "RequestId": "3D57EAD2-8723-1F26-B69C-F8707D8B565D" }

其中Credentials中的信息即为STS Token。

步骤六:使用STS Token访问阿里云资源

使用从步骤五获取的STS Token访问有权限的阿里云资源。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3